package cool.mtc.security;

import cool.mtc.security.auth.AuthConfig;
import cool.mtc.security.auth.custom.CustomAuthAdapter;
import cool.mtc.security.auth.jwt.JwtAuthAdapter;
import cool.mtc.security.auth.password.PasswordAuthAdapter;
import cool.mtc.security.config.DefaultSecurityFilterChain;
import cool.mtc.security.config.CustomSecurityFilterChain;
import cool.mtc.security.handler.HandlerConfig;
import cool.mtc.security.handler.deny.AccessDeniedHandler;
import cool.mtc.security.handler.deny.AuthenticationEntryPoint;
import cool.mtc.security.handler.logout.LogoutHandler;
import cool.mtc.security.handler.logout.LogoutSuccessHandler;
import cool.mtc.security.plugin.jwt.JwtConfig;
import cool.mtc.security.plugin.jwt.JwtProperties;
import cool.mtc.security.service.PermissionService;
import cool.mtc.security.service.impl.PermissionServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.web.SecurityFilterChain;

/**
 * @author 明河
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
@ConditionalOnProperty(value = "mtc.security.enabled", havingValue = "true", matchIfMissing = true)
@EnableConfigurationProperties({
        SecurityProperties.class,
        JwtProperties.class
})
@Import({
        JwtConfig.class,
        AuthConfig.class,
        HandlerConfig.class,
})
@Setter(onMethod = @__(@Autowired))
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class SecurityAutoConfiguration {
    private final ApplicationContext context;
    private final SecurityProperties securityProperties;

    @Bean
    @ConditionalOnMissingBean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.csrf().disable();
        // 跨域
        if (securityProperties.isCors()) {
            http.cors();
        } else {
            http.cors().disable();
        }

        // 自定义登录方式适配器
        if (securityProperties.getAuthCustom().isEnabled()) {
            http.apply(context.getBean(CustomAuthAdapter.class));
        }
        // Jwt认证适配器
        if (securityProperties.getAuthJwt().isEnabled()) {
            http.apply(context.getBean(JwtAuthAdapter.class));
        }
        // 用户名密码登录适配器
        if (securityProperties.getAuthPassword().isEnabled()) {
            http.apply(context.getBean(PasswordAuthAdapter.class));
        }

        // 需要认证的地址
        http.authorizeRequests()
                .antMatchers(securityProperties.getAuthAntPatterns())
                .authenticated();
        // 登出
        http.logout()
                .clearAuthentication(false)
                .logoutUrl(securityProperties.getLogoutUrl())
                .addLogoutHandler(context.getBean(LogoutHandler.class))
                .logoutSuccessHandler(context.getBean(LogoutSuccessHandler.class));
        // 异常处理
        http.exceptionHandling()
                .authenticationEntryPoint(new AuthenticationEntryPoint())
                .accessDeniedHandler(new AccessDeniedHandler());

        // 自定义配置
        customSecurityFilterChain().configure(http);

        return http.build();
    }

    @Bean
    @ConditionalOnMissingBean
    public CustomSecurityFilterChain customSecurityFilterChain() {
        return new DefaultSecurityFilterChain();
    }

    @Bean
    @ConditionalOnMissingBean
    public WebSecurityCustomizer webSecurityCustomizer() {
        // 忽略认证
        if (securityProperties.getIgnoreAuthAntPatterns().length > 0) {
            return web -> web.ignoring().antMatchers(securityProperties.getIgnoreAuthAntPatterns());
        }
        return web -> {
        };
    }

    @Bean(name = "ps")
    public PermissionService permissionService() {
        return new PermissionServiceImpl();
    }

}
